package com.yet.container.registry;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;

import com.yet.container.Container;
import com.yet.container.registry.RegistryMeta;
import com.yet.container.registry.RegistryMeta.ServiceMeta;
import com.yet.dto.Request;
import com.yet.dto.Response;
import com.yet.exception.RegistryNotFoundException;
import com.yet.exception.ServiceException;
import com.yet.listener.ServerResponseEvent;
import com.yet.listener.ServerStopedEvent;

/**
 * 服务曝光方的服务详情保管
 */
public class LocalServiceStateHolder implements ApplicationListener<ApplicationEvent>, Container {

	private static final Logger logger = LoggerFactory.getLogger(LocalServiceStateHolder.class);

	public static final Map<ServiceMeta, ServiceDetails> service_details = new ConcurrentHashMap<>();

	private final Map<String, RegistryCenter> registryCentersHolder;

	private volatile State state = new State("本地服务容器", true);

	public LocalServiceStateHolder(Map<String, RegistryCenter> registryCenters) {
		this.registryCentersHolder = registryCenters.values().stream()
				.collect(Collectors.toMap(rc -> rc.getName(), rc -> rc));
	}

	/**
	 * 获取服务的执行对象
	 * 
	 * @param serviceMeta
	 *            服务描述信息
	 * @return 对象
	 */
	public static Object getService(final ServiceMeta serviceMeta) {
		ServiceDetails deatils = Optional.ofNullable(service_details.get(serviceMeta))
				.orElseThrow(() -> new RegistryNotFoundException(serviceMeta.toString()));
		if (deatils.getState() == ServiceDetails.Type.OFF) {
			throw new ServiceException("服务已下线:" + serviceMeta);
		}
		return deatils.getBean();
	}

	/**
	 * 注册到本地 和 以及注册中心
	 * 
	 * @param registryMeta
	 *            注册信息
	 * @param object
	 *            执行对象
	 */
	public synchronized void registry(RegistryMeta registryMeta, Object object) {
		service_details.putIfAbsent(registryMeta.getServiceMeta(), new ServiceDetails(registryMeta, object));
		getRegistryCenter(registryMeta.getServiceMeta().getRegistryCenterName()).register(registryMeta);
	}

	/**
	 * 取消注册服务
	 * 
	 * @param registryMeta
	 *            注册信息
	 */
	public synchronized void unRegistry(RegistryMeta registryMeta) {
		service_details.remove(registryMeta.getServiceMeta());
		getRegistryCenter(registryMeta.getServiceMeta().getRegistryCenterName()).unregister(registryMeta);
	}

	/**
	 * 注销所有服务
	 * 
	 * @param registryMeta
	 *            注册信息
	 */
	public synchronized void cancelAllRegistryService() {
		service_details.values().stream().map(d -> d.getRegistryMeta()).forEach(this::unRegistry);
	}

	/**
	 * 下架服务
	 * 
	 * @param serviceMeta
	 */
	public void offRegistry(ServiceMeta serviceMeta) {
		Optional.ofNullable(service_details.get(serviceMeta)).map(d -> d.offService())
				.orElseThrow(() -> new RegistryNotFoundException(serviceMeta.toString()));
	}

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		/** 服务响应事件 */
		if (event instanceof ServerResponseEvent) {
			serverResponseHandler((ServerResponseEvent) event);
		}
		/** 服务停止事件 */
		if (event instanceof ServerStopedEvent) {
			serverStopedHandler((ServerStopedEvent) event);
		}
	}

	/**
	 * 处理服务调用的统计信息
	 * 
	 * @param event
	 */
	private void serverResponseHandler(ServerResponseEvent event) {
		Response resp = event.getResp();
		Request req = event.getReq();
		ServiceMeta serviceMeta = req.getServiceMeta();
		ServiceDetails localServiceDeatils = Optional.ofNullable(service_details.get(serviceMeta))
				.map(d -> d.incrUseCount()).orElseThrow(() -> new RegistryNotFoundException(serviceMeta.toString()));
		if (resp.getThrowable() == null) {
			localServiceDeatils.incrSucCountt();
		}
	}

	/**
	 * 下线服务
	 */
	private void serverStopedHandler(ServerStopedEvent event) {
		ServiceMeta serviceMeta = (ServiceMeta) event.getSource();
		logger.error("{} 服务暂停，下线处理", serviceMeta);
		offRegistry(serviceMeta);
	}

	/**
	 * 获取所有的服务
	 */
	public List<RegistryMeta> getAllServices() {
		return service_details.values().stream().map(d -> d.getRegistryMeta()).collect(Collectors.toList());
	}

	public void clear() {
		service_details.clear();
	}

	@Override
	public void start() {
	}

	@Override
	public void stop() {
		cancelAllRegistryService();
		clear();
		state.setStarted(false);
	}

	private RegistryCenter getRegistryCenter(String name) {
		if (StringUtils.isNotBlank(name)) {
			return registryCentersHolder.get(name);
		}
		if (registryCentersHolder.size() == 1) {
			return registryCentersHolder.values().iterator().next();
		}
		throw new IllegalArgumentException("存在多个注册中心,请选择其中一个");
	}

	@Override
	public State getState() {
		state.setInfo(service_details.toString());
		return state;
	}

	public static class ServiceDetails implements Serializable {

		private static final long serialVersionUID = 1L;

		private RegistryMeta registryMeta;

		private Object bean;

		private Type state = Type.UP;

		private long useCount = 0L;

		private long sucCount = 0L;

		public ServiceDetails(RegistryMeta registryMeta, Object bean, Type state) {
			this.registryMeta = registryMeta;
			this.state = state;
			this.bean = bean;
		}

		public ServiceDetails(RegistryMeta registryMeta, Object bean) {
			this(registryMeta, bean, Type.UP);
		}

		public static enum Type {
			UP, OFF
		}

		public Type getState() {
			return state;
		}

		public ServiceDetails offService() {
			this.state = Type.OFF;
			return this;
		}

		public long getUseCount() {
			return useCount;
		}

		public void setUseCount(long useCount) {
			this.useCount = useCount;
		}

		public ServiceDetails incrUseCount() {
			this.useCount++;
			return this;
		}

		public long getSucCount() {
			return sucCount;
		}

		public ServiceDetails incrSucCountt() {
			this.sucCount++;
			return this;
		}

		public void setSucCount(long sucCount) {
			this.sucCount = sucCount;
		}

		public RegistryMeta getRegistryMeta() {
			return registryMeta;
		}

		public Object getBean() {
			return bean;
		}

		public void setBean(Object bean) {
			this.bean = bean;
		}

		@Override
		public String toString() {
			return "ServiceDetails [state=" + state + ", useCount=" + useCount + ", sucCount=" + sucCount + "]";
		}

	}

	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE;
	}
}
